import fnmatch
import mimetypes
import os

from Server import var


class Type:
    NONE = 0
    IMAGE = 1
    AUDIO = 3


TypeListDict = {
    "image": [
        {
            "name": ".jpg",
            "mime": "image/jpeg"
        }
    ],
    "audio": [
        {
            "name": ".mp3",
            "mime": "audio/mpeg"
        }
    ],
    "other": [
        {
            "name": ".apk",
            "mime": "application/vnd.android.package-archive"
        }
    ]
}


def getFileSuffix(filename: str) -> str:
    """
    获取文件后缀名（扩展名），小写字母格式

    :param filename: 文件名
    :return: 后缀名为空则返回空；否则返回 以 '.' 开头的后缀名
    """
    fl = filename.split('.')
    if len(fl) <= 1:
        return ''
    return '.' + fl[-1].lower()


def setFileSuffix(filename: str, newSuffix: str) -> str:
    """
    设置新的文件后缀名（不改变磁盘中该文件）

    :param filename: 原文件名
    :param newSuffix: 新后缀名，需要以 '.' 开头
    :return: 新文件名
    """
    if not newSuffix:
        newSuffix = ''
    elif newSuffix[0] != '.':
        newSuffix = '.' + newSuffix
    oldSuffix = getFileSuffix(filename)
    if not oldSuffix:
        return filename + newSuffix
    return filename[:-1 * len(oldSuffix)] + newSuffix


def getFileDir(filepath: str) -> [str, str]:
    """
    获取文件所在目录的绝对路径

    :param filepath: 文件路径（绝对路径）
    :return: [父目录, 当前文件名]
    """
    # filepath = filepath.replace('\\', '/')
    # l = len(filepath.split('/')[-1])
    # return filepath[:-1 * l - 1]
    # return os.path.dirname(filepath)
    return var.__getParentPath(filepath)


def getFileName(filepath: str) -> str:
    """
    获取文件名，不包含路径

    :param filepath: 文件路径（绝对路径）
    :return:
    """
    # filepath = filepath.replace('\\', '/')
    # return filepath.split('/')[-1]
    return os.path.basename(filepath)


def deleteFile(filepath: str):
    """
    删除文件

    :param filepath: 文件路径
    :return:
    """
    if os.path.exists(filepath):
        os.remove(filepath)


def isInTypes(filename: str, types: list) -> bool:
    """
    判断文件名是否是列表中的某个类型

    :param filename: 文件名
    :param types: 类型列表，每个类型需要以 '.' 开头，且字母小写
    :return: 在列表中返回 True，否则 False
    """
    for t in types:
        tt = t
        if t[0] != '.':
            tt = '.' + t
        if getFileSuffix(filename) == tt.lower():
            return True
    return False


def getFileType(fileName: str) -> int:
    """
    获取文件类型，可返回的类型在 class Type 中

    :param fileName:
    :return: Server.Utils.fileType.Type. { NONE | IMAGE | VIDEO | AUDIO }
    """
    t = getFileSuffix(fileName)
    if not t:
        return Type.NONE
    for ty in TypeListDict['image']:
        if t == ty['name']:
            return Type.IMAGE
    for ty in TypeListDict['audio']:
        if t == ty['name']:
            return Type.AUDIO
    return Type.NONE


def getContentType(filePath: str) -> str:
    """
    获取文件的 Content-Type，默认返回 'text/plain'

    :param filePath:
    :return:
    """
    t = getFileSuffix(filePath)
    if not t:
        return 'text/plain'
    for ty in TypeListDict['image']:
        if t == ty['name']:
            return ty['mime']
    for ty in TypeListDict['audio']:
        if t == ty['name']:
            return ty['mime']
    for ty in TypeListDict['other']:
        if t == ty['name']:
            return ty['mime']
    # 其他类型则猜测
    kind = mimetypes.guess_type(filePath)[0]
    if not kind:
        return 'application/octet-stream'
    return kind


def isHiddenPath(path: str, hiddenConfig: dict) -> bool:
    """
    判断某个文件或目录的路径是否是需要隐藏的路径

    :param path: 文件或目录的路径
    :param hiddenConfig: 字典类型，共三个字段：
        1、dirs[list]：需要隐藏的目录列表，支持通配符匹配（*?等）
        2、files[list]：需要隐藏的目录列表，支持通配符匹配（*?等）
        3、types[list]：需要隐藏的文件类型列表
    :return:
    """
    if os.path.isdir(path):
        for d in hiddenConfig['dirs']:
            if path.startswith(d) or fnmatch.fnmatch(path, d):
                return True
    else:
        for d in hiddenConfig['dirs']:
            if path.startswith(d + '/'):
                return True
        if isInTypes(path, hiddenConfig['types']):
            return True
        for pattern in hiddenConfig['files']:
            if fnmatch.fnmatch(path, pattern):
                return True
    return False


def routeOrPathEquals(p1: str, p2: str) -> bool:
    """
    判断路径或路由是否相等

    :param p1:
    :param p2:
    :return:
    """
    if not p1.endswith('/'):
        p1 += '/'
    if not p2.endswith('/'):
        p2 += '/'
    return p1 == p2
